package com.jia.train.seat.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jia.train.global.page.PageResponse;
import com.jia.train.global.utils.SnowIdUtil;
import com.jia.train.seat.entity.DailyTrainTicket;
import com.jia.train.seat.entity.DailyTrainTicketExample;
import com.jia.train.seat.entity.TripStation;
import com.jia.train.seat.enums.SeatTypeEnum;
import com.jia.train.seat.mapper.DailyTrainSeatMapper;
import com.jia.train.seat.mapper.DailyTrainTicketMapper;
import com.jia.train.seat.pojo.GroupBy;
import com.jia.train.seat.request.DailyTrainTicketQueryRequest;
import com.jia.train.seat.response.DailyTrainTicketQueryResponse;
import com.jia.train.seat.service.DailyTrainTicketService;
import com.jia.train.seat.service.TripStationService;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DailyTrainTicketServiceImpl implements DailyTrainTicketService {

    @Resource
    private DailyTrainTicketMapper dailyTrainTicketMapper;

    @Resource
    private DailyTrainSeatMapper dailyTrainSeatMapper;

    @Resource
    private TripStationService tripStationService;

    public static final Logger logger = LoggerFactory.getLogger(DailyTrainTicketServiceImpl.class);


    @Override
    public void genDailyTrainTicket(Date date, String tripCode) {

        logger.info("生成日期：{},车次: {} 的余票信息", DateUtil.formatDate(date), tripCode);
        DateTime now = DateTime.now();

        List<TripStation> list = tripStationService.selectByTripCode(tripCode);

        if (CollectionUtils.isEmpty(list)) {
            logger.info("当前车次没有车次车站数据，结束");
            return;
        }

        // 查询座位数量
        List<GroupBy> group = dailyTrainSeatMapper.countSeatTypeGroup(date, tripCode);
        if (group.isEmpty()) {
            logger.info("当前车次没有座位数据，结束");
            return;
        }
        Map<String, Number> countMap = group.stream()
                .collect(Collectors.toMap(GroupBy::getSeatType, GroupBy::getSeatCount));


        List<DailyTrainTicket> dailyTrainTicketList = new ArrayList<>();
        DailyTrainTicket dailyTrainTicket;
        for (int i = 0; i < list.size(); i++) {
            BigDecimal sumKM = BigDecimal.ZERO;
            TripStation tripStationStart = list.get(i);
            for (int i1 = (i + 1); i1 < list.size(); i1++) {
                TripStation tripStationEnd = list.get(i1);
                sumKM = sumKM.add(tripStationEnd.getKm());
                //
                dailyTrainTicket = new DailyTrainTicket();
                dailyTrainTicket.setId(SnowIdUtil.getLongSnowNextId());
                dailyTrainTicket.setDate(date);
                dailyTrainTicket.setTripCode(tripCode);
                // 初始
                dailyTrainTicket.setStart(tripStationStart.getName());
                dailyTrainTicket.setStartPinyin(tripStationStart.getNamePinyin());
                dailyTrainTicket.setStartTime(tripStationStart.getOutTime());
                dailyTrainTicket.setStartIndex(ByteUtil.intToByte(tripStationStart.getIndex()));
                // 末尾
                dailyTrainTicket.setEnd(tripStationEnd.getName());
                dailyTrainTicket.setEndPinyin(tripStationEnd.getNamePinyin());
                dailyTrainTicket.setEndTime(tripStationEnd.getInTime());
                dailyTrainTicket.setEndIndex(ByteUtil.intToByte(tripStationEnd.getIndex()));
                // 座位
                dailyTrainTicket.setYdz(countMap.get(SeatTypeEnum.YDZ.getCode()).intValue());
                dailyTrainTicket.setYdzPrice(SeatTypeEnum.YDZ.getPriceRate().multiply(sumKM));
                dailyTrainTicket.setEdz(countMap.get(SeatTypeEnum.EDZ.getCode()).intValue());
                dailyTrainTicket.setEdzPrice(SeatTypeEnum.EDZ.getPriceRate().multiply(sumKM));
                dailyTrainTicket.setRw(countMap.get(SeatTypeEnum.RW.getCode()).intValue());
                dailyTrainTicket.setRwPrice(SeatTypeEnum.RW.getPriceRate().multiply(sumKM));
                dailyTrainTicket.setYw(countMap.get(SeatTypeEnum.YW.getCode()).intValue());
                dailyTrainTicket.setYwPrice(SeatTypeEnum.YW.getPriceRate().multiply(sumKM));
                dailyTrainTicket.setCreateTime(now);
                // 加入
                dailyTrainTicketList.add(dailyTrainTicket);
            }
        }


        DailyTrainTicketExample dailyTrainTicketExample = new DailyTrainTicketExample();
        dailyTrainTicketExample.createCriteria()
                .andDateEqualTo(date)
                .andTripCodeEqualTo(tripCode);

        dailyTrainTicketMapper.deleteByExample(dailyTrainTicketExample);
        dailyTrainTicketMapper.batchInsert(dailyTrainTicketList);

    }

    @Override
    @Cacheable(value = "DailyTrainTicketService.selectPage")
    public PageResponse<DailyTrainTicketQueryResponse> selectPage(DailyTrainTicketQueryRequest request) {
        DailyTrainTicketExample dailyTrainTicketExample = new DailyTrainTicketExample();
        // 排序
        dailyTrainTicketExample.setOrderByClause("date desc,trip_code asc");
        // 条件
        DailyTrainTicketExample.Criteria criteria = dailyTrainTicketExample.createCriteria();
        String tripCode = request.getTripCode();
        Date date = request.getDate();
        String start = request.getStart();
        String end = request.getEnd();
        if (StringUtils.hasText(tripCode)) {
            criteria.andTripCodeEqualTo(tripCode);
        }
        if (ObjectUtil.isNotNull(date)) {
            criteria.andDateEqualTo(date);
        }
        if (StringUtils.hasText(start)) {
            criteria.andStartEqualTo(start);
        }
        if (StringUtils.hasText(end)) {
            criteria.andEndEqualTo(end);
        }
        // 构造分页
        PageHelper.startPage(request.getPage(), request.getSize());
        List<DailyTrainTicket> DailyTrainTickets = dailyTrainTicketMapper.selectByExample(dailyTrainTicketExample);
        PageInfo<DailyTrainTicket> pageInfo = new PageInfo<>(DailyTrainTickets);

        List<DailyTrainTicketQueryResponse> list
                = BeanUtil.copyToList(DailyTrainTickets, DailyTrainTicketQueryResponse.class);

        PageResponse<DailyTrainTicketQueryResponse> pageResponse = new PageResponse<>();
        pageResponse.setTotal(pageInfo.getTotal());
        pageResponse.setList(list);
        return pageResponse;
    }

    public DailyTrainTicket selectByUnique(String tripCode, Date date,
                                           String start, String end) {
        DailyTrainTicketExample ticketExample = new DailyTrainTicketExample();
        DailyTrainTicketExample.Criteria criteria = ticketExample.createCriteria();
        criteria.andDateEqualTo(date);
        criteria.andTripCodeEqualTo(tripCode);
        criteria.andStartEqualTo(start);
        criteria.andEndEqualTo(end);

        List<DailyTrainTicket> dailyTrainTicketList
                = dailyTrainTicketMapper.selectByExample(ticketExample);
        if (!CollectionUtils.isEmpty(dailyTrainTicketList)) {
            return dailyTrainTicketList.get(0);
        }
        return null;
    }

    @Override
    @CachePut(value = "DailyTrainTicketService.selectPage")
    public void deleteTicket() {
        // 缓存击穿(有数据但过期) 一个热点的Key失效后 导致大量请求直接访问数据库
        // 失效问题 主动更新一次 定时任务 每30秒主动调用一次
        // 针对数据库查询 增加分布式锁 100个请求 一个拿锁 其他99个主动失败
        // 缓存机器坏了 就没办法解决

        // 缓存穿透（没数据硬查）
//        if(有数据){
//            return;
//        }else {
//            去数据库取数据
//        }
        // 针对数据库查询 增加分布式锁 100个请求 一个拿锁 其他99个主动失败
        // 判断空 空列表 空就去查 空列表就直接缓存上


        // 缓存雪崩 大量Key失效
        // 常见的缓存过期策略
        // TTL 超时时间
        // LRU 最近最少使用（时间）LFU 最近最不经常使用（次数）FIFO 先进先出 Random 随机淘汰策略
        // 加锁无效 使用限流

        // 常见的生产场景
        // 场景： 大量的会员信息 但是会员请求量少 而且请求体庞大
        // 原因 多表信息组装 库被访问多次 5次*5表 = 25次数据库
        // 方案 使用本地缓存 1分钟 容易频繁fullGc
        // 去掉本地缓存 使用线程本地变量

    }

}
